package com.rmfyzxfw.caseprocess.service.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.rmfyzxfw.caseprocess.enums.*;
import com.rmfyzxfw.caseprocess.manager.CaseManager;
import com.rmfyzxfw.caseprocess.mapper.*;
import com.rmfyzxfw.caseprocess.model.cmd.ProcessNodeCmd;
import com.rmfyzxfw.caseprocess.model.dto.RuleItemDTO;
import com.rmfyzxfw.caseprocess.model.dynamic.query.CommonFieldQry;
import com.rmfyzxfw.caseprocess.model.param.ZXCaseInfoListParam;
import com.rmfyzxfw.caseprocess.model.po.Process;
import com.rmfyzxfw.caseprocess.model.po.*;
import com.rmfyzxfw.caseprocess.model.vo.ProcessNodeVO;
import com.rmfyzxfw.caseprocess.service.CaseProcessService;
import com.rmfyzxfw.caseprocess.util.DynamicDataSourceUtil;
import com.rmfyzxfw.caseprocess.util.RuleExpressionUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;

import javax.script.ScriptException;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * @description:
 * @Author: GYW  742926306@qq.com
 * @Date: 2024/9/1 11:36
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class CaseProcessServiceImpl implements CaseProcessService {
    private final DynamicDataSourceMapper dynamicDataSourceMapper;
    private final EngineDataDynamicTableMySqlMapper engineDataDynamicTableMySqlMapper;
    private final EngineDataDynamicTableOracleMapper engineDataDynamicTableOracleMapper;
    private final ZXCaseMapper zxCaseMapper;
    private final ProcessMapper processMapper;
    private final ProcessDataMapper processDataMapper;
    private final CaseProcessNodeMapper caseProcessNodeMapper;
    private final CaseDataNodeMapper caseDataNodeMapper;
    private final CaseDataNodeStatusMapper caseDataNodeStatusMapper;
    private final FymcMapper fymcMapper;
    private final CaseManager caseManager;

    /**
     * 更新或生成案件流程节点及数据节点状态  审判
     */
    public void caseProcessSP() {
        //获取数据源（审判、执行）
        // 1.循环遍历案件基本表  分页查询 按案件标识排序 游标 （审判、执行分两个线程，如果基本表结构不一致需要分两个线程）
        //  案件类型（金钱给付、财产保全）
    }

    /**
     * 更新或生成案件流程节点及数据节点状态  执行
     */
    public void caseProcessZXBak() {
        // 1.循环遍历执行案件基本表  分页查询 按案件标识排序 游标
        // 2.查找t_case_process_node表，如果有数据，再查询流程表（根据法院、案件类型（金钱给付、财产保全）确认流程，如果没有则使用默认流程），
        // 比较流程数据id是否一致，一致则运行该规则，不一致则先删除对应数据再重新生成（t_case_data_node和查找t_case_process_node表）；
        // 如果没有数据直接生成（t_case_data_node和查找t_case_process_node表）

        DynamicDataSource dynamicDataSource = dynamicDataSourceMapper.selectOne(Wrappers.<DynamicDataSource>lambdaQuery().eq(DynamicDataSource::getDatabaseType, 1).last("limit 1"));
        ZXCaseInfoListParam param = new ZXCaseInfoListParam();
        param.setPageSize(100L);
        Page<ZXCaseInfo> page = new Page<>(param.getPageNum(), param.getPageSize());
        page.addOrder(OrderItem.asc("ajbs"));
        Page<ZXCaseInfo> zxCaseInfoPage = DynamicDataSourceUtil.route(dynamicDataSource.getDynamicDataSourceId(), () -> zxCaseMapper.selectPage(page, Wrappers.<ZXCaseInfo>lambdaQuery()));
        List<ZXCaseInfo> zxCaseInfos = zxCaseInfoPage.getRecords();
        updateMultiCaseProcessStatus(zxCaseInfos);
        while (CollectionUtils.isNotEmpty(zxCaseInfos)) {
            // 遍历案件
            param.setPageNum(param.getPageNum() + 1);
            page.setCurrent(param.getPageNum());

            param.setAjbs(zxCaseInfos.get(zxCaseInfos.size() - 1).getAjbs());
            zxCaseInfoPage = DynamicDataSourceUtil.route(dynamicDataSource.getDynamicDataSourceId(), () -> zxCaseMapper.selectPage(page, Wrappers.<ZXCaseInfo>lambdaQuery().gt(ZXCaseInfo::getAjbs, param.getAjbs())));
            zxCaseInfos = zxCaseInfoPage.getRecords();
            updateMultiCaseProcessStatus(zxCaseInfos);
        }

    }

    @Override
    public void caseProcessZX() {
        DynamicDataSource dynamicDataSource = dynamicDataSourceMapper.selectOne(Wrappers.<DynamicDataSource>lambdaQuery().eq(DynamicDataSource::getDataSourceType, 1).last("limit 1"));
        ZXCaseInfoListParam param = new ZXCaseInfoListParam();
        param.setPageSize(100L);
        Page<ZXCaseInfo> page = new Page<>(param.getPageNum(), param.getPageSize());
        page.addOrder(OrderItem.asc("ajbs"));
        List<ZXCaseInfo> zxCaseInfos;
        do {
            // 遍历案件
            Page<ZXCaseInfo> zxCaseInfoPage = DynamicDataSourceUtil.route(dynamicDataSource.getDynamicDataSourceId()
                    , () -> zxCaseMapper.selectPage(page, Wrappers.<ZXCaseInfo>lambdaQuery().gt(StringUtils.isNotBlank(param.getAjbs()), ZXCaseInfo::getAjbs, param.getAjbs())));
            zxCaseInfos = zxCaseInfoPage.getRecords();
            if (CollectionUtils.isEmpty(zxCaseInfos)) {
                break;
            }
            updateMultiCaseProcessStatus(zxCaseInfos);

            param.setPageNum(param.getPageNum() + 1);
            page.setCurrent(param.getPageNum());
            param.setAjbs(zxCaseInfos.get(zxCaseInfos.size() - 1).getAjbs());
        } while (CollectionUtils.isNotEmpty(zxCaseInfos));
    }

    @Override
    public void updateMultiCaseProcessStatus(List<ZXCaseInfo> zxCaseInfos) {
        // 不使用事务，判断插入或更新
        for (ZXCaseInfo zxCaseInfo : zxCaseInfos) {
            try {
                updateSingleCaseProcessStatus(zxCaseInfo);
            } catch (Exception e) {
                log.error("案件 {} 执行流程失败", zxCaseInfo.getAh(), e);
            }

        }
    }

    @Override
    public void updateSingleCaseProcessStatus(ZXCaseInfo zxCaseInfo) {
        // TODO 表t_fymc  dm_ajzh等字典表在主库放一份
        // TODO 是否要加分布式锁 防止数据重复插入 查询的是否也要加锁？
        // 查询对应的流程，更新状态是只更新本流程
        caseManager.setCaseType(zxCaseInfo);
        List<Process> processes = processMapper.selectList(Wrappers.<Process>lambdaQuery()
                .eq(Process::getCourtId, zxCaseInfo.getFydm())
                .eq(Process::getDataSourceType, DataSourceTypeEnum.ZX.getType())
                .eq(Process::getCaseType, zxCaseInfo.getCaseType())
                .eq(Process::getStatus, EnableStatusEnum.ENABLE.getCode())
                .isNotNull(Process::getProcessDataId)
                .ne(Process::getProcessDataId, Strings.EMPTY));

        if (StringUtils.isEmpty(zxCaseInfo.getCaseType())) {
            return;
        }
        Fymc fymc = fymcMapper.selectOne(Wrappers.<Fymc>lambdaQuery().eq(Fymc::getFydmHy, zxCaseInfo.getFydm()));
        if (CollectionUtils.isEmpty(processes)) { //法院没有配置流程 使用高院配置的流程
            // 获取高院流程

            processes = processMapper.selectList(Wrappers.<Process>lambdaQuery()
                    .eq(Process::getCourtId, fymc.getGydm())
                    .eq(Process::getDataSourceType, DataSourceTypeEnum.ZX.getType())
                    .eq(Process::getCaseType, zxCaseInfo.getCaseType())
                    .eq(Process::getStatus, EnableStatusEnum.ENABLE.getCode())
                    .isNotNull(Process::getProcessDataId)
                    .ne(Process::getProcessDataId, Strings.EMPTY));
        } else if (!fymc.getFydmHy().equals(fymc.getGydm())) { // 删除非高院的 其他法院之前使用高院的流程创建的
            // 本院配置了流程，删除之前使用高院流程生成的数据
            // 删除 t_case_data_node_status
            List<CaseDataNode> caseDataNodes = caseDataNodeMapper.selectList(Wrappers.<CaseDataNode>lambdaQuery()
                    .eq(CaseDataNode::getAh, zxCaseInfo.getAh())
                    .eq(CaseDataNode::getCourtId, fymc.getGydm())
                    .select(CaseDataNode::getCaseDataNodeId));
            if (CollectionUtils.isNotEmpty(caseDataNodes)) {
                caseDataNodeStatusMapper.delete(Wrappers.<CaseDataNodeStatus>lambdaQuery()
                        .in(CaseDataNodeStatus::getCaseDataNodeId, caseDataNodes.stream().map(CaseDataNode::getCaseDataNodeId).collect(Collectors.toList())));
                // 删除 t_case_data_node
                caseDataNodeMapper.deleteBatchIds(caseDataNodes.stream().map(CaseDataNode::getId).collect(Collectors.toList()));
            }

            // 删除 t_case_process_node
            caseProcessNodeMapper.delete(Wrappers.<CaseProcessNode>lambdaQuery()
                    .eq(CaseProcessNode::getAh, zxCaseInfo.getAh())
                    .eq(CaseProcessNode::getCourtId, fymc.getGydm()));

        }

        for (Process process : processes) {// 遍历符合条件流程
            ProcessData processData = processDataMapper.selectOne(Wrappers.<ProcessData>lambdaQuery().eq(ProcessData::getProcessDataId, process.getProcessDataId()));
            // 所有的数据先判断是否库里有，再插入或更新
            // 同一数据节点在一个流程只出现一次
            List<ProcessNodeVO> processNodeVOS = JSON.parseArray(processData.getData(), ProcessNodeVO.class);
            // 数据插入前判断是否存在 存在则更新
            for (ProcessNodeVO processNodeVO : processNodeVOS) {// 流程节点数据
                List<ProcessNodeVO.DataNodeVO> dataNodeVOS = processNodeVO.getDataNodes();
                /* 缓存所有节点所有状态的计算结果  目前是直接取的数据中的值 已弃用*/
                Map<String, List<NodeStatusResult>> nodeStatusResultMap = Maps.newHashMap();
                // 流程节点配置的表达式子项结果
                List<RuleItemDTO> processNodeVORuleItemDTOS = Lists.newArrayList();
                // 遍历数据节点
                iterateDataNodes(zxCaseInfo, process, processNodeVO, dataNodeVOS, nodeStatusResultMap, processNodeVORuleItemDTOS);

                // 计算流程节点表达式
                boolean processNodeVOVOResult;
                if (processNodeVO.getFinishAll()) {
                    //  完成所有节点 不需要计算表达式  状态 finish=1
                    processNodeVOVOResult = processNodeVORuleItemDTOS.stream().allMatch(RuleItemDTO::isResult);
                } else {
                    try {
                        processNodeVOVOResult = RuleExpressionUtil.evalGroovy(processNodeVO.getRuleExpression(), processNodeVORuleItemDTOS);
                        //  创建 t_case_process_node

                    } catch (ScriptException e) {
                        log.error("流程节点计算失败 {} {} ", process.getProcessDataId(), processNodeVO.getProcessNodeId());
                        throw new RuntimeException(e);
                    }
                }

                saveCaseProcessNode(zxCaseInfo, process, processNodeVO, processNodeVOVOResult);
                // TODO 如果计算结果为false代表不能流转到下一个流程节点，break（暂时不做）
            }
        }

    }


    /**
     * 遍历数据节点
     *
     * @param zxCaseInfo
     * @param process
     * @param processNodeVO
     * @param dataNodeVOS
     * @param nodeStatusResultMap
     * @param processNodeVORuleItemDTOS
     */
    private void iterateDataNodes(ZXCaseInfo zxCaseInfo, Process process, ProcessNodeVO processNodeVO, List<ProcessNodeVO.DataNodeVO> dataNodeVOS, Map<String, List<NodeStatusResult>> nodeStatusResultMap, List<RuleItemDTO> processNodeVORuleItemDTOS) {
        for (ProcessNodeVO.DataNodeVO dataNodeVO : dataNodeVOS) {
            // 缓存本节点所有状态计算结果
            List<NodeStatusResult> nodeStatusResults = Lists.newArrayList();
            nodeStatusResultMap.put(dataNodeVO.getDataNodeId(), nodeStatusResults);
            // 创建t_case_data_node
            CaseDataNode caseDataNode = saveCaseDataNode(zxCaseInfo, process, processNodeVO, dataNodeVO);
            boolean nodeResult = false;
            for (ProcessNodeVO.DataNodeStatusVO dataNodeStatusVO : dataNodeVO.getDataNodeStatuses()) { // 所有的状态计算
                List<ProcessNodeVO.NodeFieldVO> nodeFieldVOS = dataNodeStatusVO.getNodeFields();
                String dataNodeStatusVORuleExpression = dataNodeStatusVO.getRuleExpression();
                // 表达式为空代表所属程序触发即点亮 不需要计算字段
                boolean dataNodeStatusVOResult;
                if (StringUtils.isBlank(dataNodeStatusVORuleExpression)) { // 所属流程节点触发即可
                    dataNodeStatusVOResult = true;
                } else {
                    List<RuleItemDTO> dataNodeStatusVORuleItemDTOS = Lists.newArrayList();
                    for (ProcessNodeVO.NodeFieldVO nodeFieldVO : nodeFieldVOS) { //一种状态下所有字段或节点规则
                        if (NodeFieldTypeEnum.DATA_ITEM.getType().equals(nodeFieldVO.getType())) {
                            evaluateDataItem(zxCaseInfo, dataNodeStatusVORuleItemDTOS, nodeFieldVO);
                        } else if (NodeFieldTypeEnum.DATA_NODE.getType().equals(nodeFieldVO.getType())) {
                            // 节点必须在之前配置过 已弃用
//                            boolean dataNodeResult = nodeStatusResultMap.get(nodeFieldVO.getDataNodeId())
//                                    .stream()
//                                    .filter(nsr -> nsr.getStatusId().equals(nodeFieldVO.getStatusId()))
//                                    .anyMatch(nsr -> {
//                                        try {
//                                            // 点亮且满足 配置条件 如点亮时间>= 5
//                                            return nsr.getResult() && RuleExpressionUtil.evalGroovy(nsr.getLightDate().until(LocalDate.now(), ChronoUnit.DAYS) + nodeFieldVO.getOperator() + nodeFieldVO.getLightDays());
//                                        } catch (ScriptException e) {
//                                            throw new RuntimeException(e);
//                                        }
//                                    });

                            evaluateDataNode(zxCaseInfo, process, dataNodeStatusVORuleItemDTOS, nodeFieldVO);
                        }

                    }

                    try {
                        dataNodeStatusVOResult = RuleExpressionUtil.evalGroovy(dataNodeStatusVORuleExpression, dataNodeStatusVORuleItemDTOS);
                    } catch (ScriptException e) {
                        log.error("数据节点计算失败 {} {} {} ", process.getProcessDataId(), processNodeVO.getProcessNodeId(), dataNodeStatusVO.getStatusId());
                        throw new RuntimeException(e);
                    }
                }
                // 计算完规则之后创建或更新t_case_data_node_status
                CaseDataNodeStatus caseDataNodeStatus = savaCaseDataNodeStatus(caseDataNode, dataNodeStatusVO, dataNodeStatusVOResult);

                // 缓存本节点当前状态计算结果
                nodeStatusResults.add(new NodeStatusResult(dataNodeStatusVO.getStatusId(), dataNodeStatusVOResult, caseDataNodeStatus.getLightDate()));
                if (processNodeVO.getFinishAll()) {
                    if (getStatusFinish(dataNodeStatusVO.getStatusId())) {
                        nodeResult = nodeResult || dataNodeStatusVOResult;
                    }
                } else {
                    if (StringUtils.isNotBlank(dataNodeStatusVO.getOperationItem()) && dataNodeStatusVO.getStatusId().equals(dataNodeVO.getStatusId())) {
                        // 此状态的数据节点规则用于流程节点
                        processNodeVORuleItemDTOS.add(new RuleItemDTO(dataNodeStatusVO.getOperationItem(), dataNodeStatusVOResult));
                    }
                }

            }

            // 完成所有节点时 不需要计算表达式
            if (processNodeVO.getFinishAll()) {
                processNodeVORuleItemDTOS.add(new RuleItemDTO(IdUtil.simpleUUID(), nodeResult));
            }
        }
    }

    private void evaluateDataNode(ZXCaseInfo zxCaseInfo, Process process, List<RuleItemDTO> dataNodeStatusVORuleItemDTOS, ProcessNodeVO.NodeFieldVO nodeFieldVO) {
        String processIdInner = process.getProcessId();
        String processDataIdInner = process.getProcessDataId();
        // 节点是本流程中有的 查询的时候从库里查 子流程的节点从子流程查

        /*树形嵌套时查找最后一个子流程*/
        String childrenProcessId = Strings.EMPTY;
        ProcessNodeCmd.ChildrenProcess cp = nodeFieldVO.getChildrenProcess();
        while (Objects.nonNull(cp)) {
            childrenProcessId = cp.getChildrenProcessId();
            cp = cp.getChildrenProcess();
        }

        if (StringUtils.isNotBlank(childrenProcessId)) {
            Process childrenProcess = processMapper.selectOne(Wrappers.<Process>lambdaQuery().eq(Process::getProcessId, childrenProcessId));
            processIdInner = childrenProcess.getProcessId();
            processDataIdInner = childrenProcess.getProcessDataId();
        }
        boolean dataNodeResult = false;
        // 每个节点在流程只出现一次 根据四个字段可定位一条数据
        CaseDataNode caseDataNodeInner = caseDataNodeMapper.selectOne(Wrappers.<CaseDataNode>lambdaQuery()
                .eq(CaseDataNode::getAh, zxCaseInfo.getAh())
                .eq(CaseDataNode::getProcessId, processIdInner)
                .eq(CaseDataNode::getProcessDataId, processDataIdInner)
                .eq(CaseDataNode::getDataNodeId, nodeFieldVO.getDataNodeId()));
        if (Objects.isNull(caseDataNodeInner)) {
            dataNodeStatusVORuleItemDTOS.add(new RuleItemDTO(nodeFieldVO.getOperationItem(), dataNodeResult));
            return;
        }
        CaseDataNodeStatus caseDataNodeStatus = caseDataNodeStatusMapper.selectOne(Wrappers.<CaseDataNodeStatus>lambdaQuery()
                .eq(CaseDataNodeStatus::getCaseDataNodeId, caseDataNodeInner.getCaseDataNodeId())
                .eq(CaseDataNodeStatus::getStatusId, nodeFieldVO.getStatusId()));
        if (Objects.isNull(caseDataNodeStatus)) {
            dataNodeStatusVORuleItemDTOS.add(new RuleItemDTO(nodeFieldVO.getOperationItem(), dataNodeResult));
            return;
        }
        dataNodeResult = True1False0.TRUE.getCode().equals(caseDataNodeStatus.getLightStatus());
        if (!dataNodeResult) {
            dataNodeStatusVORuleItemDTOS.add(new RuleItemDTO(nodeFieldVO.getOperationItem(), dataNodeResult));
            return;
        }
        try {
            // 点亮且满足 配置条件 如点亮时间>= 5
            dataNodeResult = RuleExpressionUtil.evalGroovy(caseDataNodeStatus.getLightDate().until(LocalDate.now(), ChronoUnit.DAYS) + nodeFieldVO.getOperator() + nodeFieldVO.getLightDays());
            dataNodeStatusVORuleItemDTOS.add(new RuleItemDTO(nodeFieldVO.getOperationItem(), dataNodeResult));
        } catch (ScriptException e) {
            log.error("数据节点DataNode计算失败 {} {} {} ", process.getProcessDataId(), nodeFieldVO.getDataNodeId(), nodeFieldVO.getStatusId());
            throw new RuntimeException(e);
        }
    }

    /**
     * 计算类型为数据项的字段结果
     *
     * @param zxCaseInfo
     * @param dataNodeStatusVORuleItemDTOS
     * @param nodeFieldVO
     */
    private void evaluateDataItem(ZXCaseInfo zxCaseInfo, List<RuleItemDTO> dataNodeStatusVORuleItemDTOS, ProcessNodeVO.NodeFieldVO nodeFieldVO) {
        CommonFieldQry commonFieldQry = CommonFieldQry.builder()
                .tableName(nodeFieldVO.getTableName())
                .fydm(zxCaseInfo.getFydm())
                .ajbh(zxCaseInfo.getAjbh())
                .condition(buildCondition(nodeFieldVO.getFieldNameEn(), nodeFieldVO.getOperator(), nodeFieldVO.getFieldTargetValue().getString("value"), nodeFieldVO.getFieldValueType()))
                .build();
        // 1.查询时使用oracle对应mapper  2.视图中基本表是否有对应字段 ajbs;ah;ajzh; 3.其他表查询时用案件标识，没有的先不管
        DynamicDataSource dynamicDataSource = dynamicDataSourceMapper.selectOne(Wrappers.<DynamicDataSource>lambdaQuery().eq(DynamicDataSource::getDynamicDataSourceId, nodeFieldVO.getDynamicDataSourceId()));
        String fydm;
        if(DynamicDatabaseTypeEnum.ORACLE.getCode().equals(dynamicDataSource.getDatabaseType())){
            fydm = DynamicDataSourceUtil.route(nodeFieldVO.getDynamicDataSourceId(), () -> engineDataDynamicTableOracleMapper.getOne(commonFieldQry));
        }else {
            fydm = DynamicDataSourceUtil.route(nodeFieldVO.getDynamicDataSourceId(), () -> engineDataDynamicTableMySqlMapper.getOne(commonFieldQry));
        }


        dataNodeStatusVORuleItemDTOS.add(new RuleItemDTO(nodeFieldVO.getOperationItem(), StringUtils.isNotBlank(fydm)));
    }

    /**
     * 构建条件并转义
     *
     * @param field
     * @param operator
     * @param value
     * @param type
     * @return
     */
    private String buildCondition(String field, String operator, String value, Integer type) {
        StringBuilder sb = new StringBuilder(field);
        sb.append(" ");
//        sb.append(String.format(GlobalConstants.Escape, operator));
        sb.append(operator);
        sb.append(" ");
        if (!Objects.equals(FieldDataTypeEnum.NUMBER.getCode(), type)) {
            sb.append("'");
            sb.append(value);
            sb.append("'");
        } else {
            sb.append(value);
        }

        return sb.toString();
    }

    private void saveCaseProcessNode(ZXCaseInfo zxCaseInfo, Process process, ProcessNodeVO processNodeVO, boolean processNodeVOVOResult) {
        // 四个字段需要建唯一索引
        CaseProcessNode caseProcessNode = caseProcessNodeMapper.selectOne(Wrappers.<CaseProcessNode>lambdaQuery()
                .eq(CaseProcessNode::getAh, zxCaseInfo.getAh())
                .eq(CaseProcessNode::getProcessNodeId, processNodeVO.getProcessNodeId())
                .eq(CaseProcessNode::getProcessId, process.getProcessId())
                .eq(CaseProcessNode::getProcessDataId, process.getProcessDataId()));

        if (Objects.isNull(caseProcessNode)) {
            caseProcessNode = new CaseProcessNode();
            caseProcessNode.setCaseProcessNodeId(IdUtil.simpleUUID());
            caseProcessNode.setAh(zxCaseInfo.getAh());
            caseProcessNode.setCourtId(zxCaseInfo.getFydm());
            caseProcessNode.setDataSourceType(DataSourceTypeEnum.ZX.getType());
            caseProcessNode.setCaseType(zxCaseInfo.getCaseType());
            caseProcessNode.setProcessDataId(process.getProcessDataId());
            caseProcessNode.setProcessId(process.getProcessId());
            caseProcessNode.setProcessNodeId(processNodeVO.getProcessNodeId());
            setCaseProcessNodeLightStatus(processNodeVOVOResult, caseProcessNode);
            caseProcessNodeMapper.insert(caseProcessNode);
            return;
        }

        setCaseProcessNodeLightStatus(processNodeVOVOResult, caseProcessNode);
        caseProcessNodeMapper.updateById(caseProcessNode);

    }

    private static void setCaseProcessNodeLightStatus(boolean processNodeVOVOResult, CaseProcessNode caseProcessNode) {
        if (processNodeVOVOResult) {
            caseProcessNode.setStatus(True1False0.TRUE.getCode());
            caseProcessNode.setLightDate(LocalDate.now());
        } else {
            caseProcessNode.setStatus(True1False0.FALSE.getCode());
        }
    }

    private CaseDataNodeStatus savaCaseDataNodeStatus(CaseDataNode caseDataNode, ProcessNodeVO.DataNodeStatusVO dataNodeStatusVO, boolean dataNodeStatusVOResult) {
        // 查询数据是否存在 存在更新 不存在新增
        // TODO 两个查询字段创建唯一索引
        CaseDataNodeStatus caseDataNodeStatusOld = caseDataNodeStatusMapper.selectOne(Wrappers.<CaseDataNodeStatus>lambdaQuery()
                .eq(CaseDataNodeStatus::getCaseDataNodeId, caseDataNode.getCaseDataNodeId())
                .eq(CaseDataNodeStatus::getStatusId, dataNodeStatusVO.getStatusId()));

        if (Objects.isNull(caseDataNodeStatusOld)) {
            CaseDataNodeStatus caseDataNodeStatus = new CaseDataNodeStatus();
            caseDataNodeStatus.setLightStatus(dataNodeStatusVOResult ? True1False0.TRUE.getCode() : True1False0.FALSE.getCode());
            caseDataNodeStatus.setLightDate(LocalDate.now());
            caseDataNodeStatus.setCaseDataNodeStatusId(IdUtil.simpleUUID());
            caseDataNodeStatus.setCaseDataNodeId(caseDataNode.getCaseDataNodeId());
            caseDataNodeStatus.setStatusId(dataNodeStatusVO.getStatusId());
            caseDataNodeStatus.setStatusLevel(dataNodeStatusVO.getStatusLevel());
            setCaseDataNodeLightStatus(dataNodeStatusVOResult, caseDataNodeStatus);

            caseDataNodeStatusMapper.insert(caseDataNodeStatus);
            return caseDataNodeStatus;
        }

        setCaseDataNodeLightStatus(dataNodeStatusVOResult, caseDataNodeStatusOld);

        caseDataNodeStatusMapper.updateById(caseDataNodeStatusOld);
        return caseDataNodeStatusOld;
    }

    private static void setCaseDataNodeLightStatus(boolean dataNodeStatusVOResult, CaseDataNodeStatus caseDataNodeStatusOld) {
        if (dataNodeStatusVOResult) {
            caseDataNodeStatusOld.setLightStatus(True1False0.TRUE.getCode());
            caseDataNodeStatusOld.setLightDate(LocalDate.now());
        } else {
            caseDataNodeStatusOld.setLightStatus(True1False0.FALSE.getCode());
        }
    }

    /**
     * 保存t_case_data_node
     *
     * @param zxCaseInfo
     * @param process
     * @param processNodeVO
     * @param dataNodeVO
     */
    private CaseDataNode saveCaseDataNode(ZXCaseInfo zxCaseInfo, Process process, ProcessNodeVO processNodeVO, ProcessNodeVO.DataNodeVO dataNodeVO) {
        // 查询数据是否存在 不存在新增
        // TODO 五个查询字段除去processNodeId创建唯一索引 数据节点在流程只能出现一次
        CaseDataNode caseDataNode = caseDataNodeMapper.selectOne(Wrappers.<CaseDataNode>lambdaQuery()
                .eq(CaseDataNode::getAh, zxCaseInfo.getAh())
                .eq(CaseDataNode::getProcessId, process.getProcessId())
                .eq(CaseDataNode::getProcessDataId, process.getProcessDataId())
                .eq(CaseDataNode::getProcessNodeId, processNodeVO.getProcessNodeId())
                .eq(CaseDataNode::getDataNodeId, dataNodeVO.getDataNodeId()));
        if (Objects.nonNull(caseDataNode)) {
            return caseDataNode;
        }
        caseDataNode = new CaseDataNode();
        caseDataNode.setCaseDataNodeId(IdUtil.simpleUUID());
        caseDataNode.setAh(zxCaseInfo.getAh());
        caseDataNode.setCourtId(zxCaseInfo.getFydm());
        caseDataNode.setDataSourceType(DataSourceTypeEnum.ZX.getType());
        caseDataNode.setCaseType(zxCaseInfo.getCaseType());
        caseDataNode.setProcessId(process.getProcessId());
        caseDataNode.setProcessDataId(process.getProcessDataId());
        caseDataNode.setProcessNodeId(processNodeVO.getProcessNodeId());
        caseDataNode.setDataNodeId(dataNodeVO.getDataNodeId());
        caseDataNodeMapper.insert(caseDataNode);

        return caseDataNode;
    }


    /**
     * // 根据statusId获取是否是完成的状态
     *
     * @param statusId
     * @return
     */
    private boolean getStatusFinish(String statusId) {
        try {
            return True1False0.TRUE.getCode().equals(StatusServiceImpl.statusCache.get(statusId).getFinish());
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    @Data
    @AllArgsConstructor
    public static class NodeStatusResult {
        private String statusId;
        private Boolean result;
        private LocalDate lightDate;
    }
}